# Create React App

This chapter introduces how to migrate a [Create React App](https://github.com/facebook/create-react-app) (CRA) or [CRACO](https://craco.js.org/) project to Rsbuild.

:::tip CRA eject
If your project has already run the CRA `eject` command, then most of the content in this document will no longer be applicable.

After ejecting a CRA project, it becomes more like a project directly using webpack, so you can refer to the [webpack migration guide](/guide/migration/webpack).

:::

## Installing Dependencies

First, you need to replace the npm dependencies of CRA with Rsbuild's dependencies.

import { PackageManagerTabs } from '@theme';

- Remove CRA dependencies:

<PackageManagerTabs command="remove react-scripts" />

> For projects using CRACO, you can also remove the @craco/craco dependency.

- Install Rsbuild dependencies:

<PackageManagerTabs command="add @rsbuild/core @rsbuild/plugin-react -D" />

## Updating npm scripts

Next, you need to update the npm scripts in package.json to Rsbuild's CLI commands.

```diff title="package.json"
{
  "scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "eject": "react-scripts eject"
+   "start": "rsbuild dev",
+   "build": "rsbuild build",
+   "preview": "rsbuild preview"
  }
}
```

:::tip
Rsbuild does not integrate testing frameworks, so it does not provide a command to replace `react-scripts test`. You can directly use testing frameworks such as Jest or Vitest. You can refer to the [Rsbuild react-jest](https://github.com/rspack-contrib/rspack-examples/tree/main/rsbuild/react-jest) example project for configuration.
:::

## Creating Configuration File

Create a Rsbuild configuration file `rsbuild.config.ts` in the same directory as package.json and add the following content:

```typescript title="rsbuild.config.ts"
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [pluginReact()],
});
```

## HTML Template

CRA uses the `public/index.html` file as the default HTML template. In Rsbuild, you can specify the HTML template through [html.template](/config/html/template):

```ts title="rsbuild.config.ts"
export default defineConfig({
  html: {
    template: './public/index.html',
  },
});
```

In the HTML template, if you are using the `%PUBLIC_URL%` variable from CRA, replace it with Rsbuild's [assetPrefix variable](/config/html/template-parameters) and use a forward slash for concatenation:

```diff
-  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+  <link rel="icon" href="<%= assetPrefix %>/favicon.ico" />
```

This completes the basic migration from CRA to Rsbuild. You can now run the `npm run start` command to try starting the dev server.

## Output Directory

By default, CRA outputs to the `build` directory, while Rsbuild's default output directory is `dist`.

You can configure Rsbuild's [output.distPath.root](/config/output/dist-path) option to change the directory to `build`, in line with CRA:

```ts title="rsbuild.config.ts"
export default {
  output: {
    distPath: {
      root: 'build',
    },
  },
};
```

> For more details, please refer to the [Output Files](/guide/basic/output-files) section.

## Using CSS Preprocessors

Rsbuild supports CSS preprocessors such as Sass and Less through plugins. Please refer to:

- [Sass Plugin](/plugins/list/plugin-sass)
- [Less Plugin](/plugins/list/plugin-less)
- [Stylus Plugin](/plugins/list/plugin-stylus)

## Using SVGR

If you are using the "SVG to React Component" feature of CRA (i.e., [SVGR](https://react-svgr.com/)), you also need to install the SVGR plugin for Rsbuild.

For example, if you are using the following usage:

```jsx
import { ReactComponent as Logo } from './logo.svg';

const App = () => (
  <div>
    <Logo />
  </div>
);
```

You only need to install and register `@rsbuild/plugin-svgr`:

```ts title="rsbuild.config.ts"
import { pluginSvgr } from '@rsbuild/plugin-svgr';

export default {
  plugins: [pluginSvgr({ mixedImport: true })],
};
```

Please refer to the [SVGR plugin](/plugins/list/plugin-svgr) documentation to learn how to use SVGR in Rsbuild.

## Configuration Migration

Here is the corresponding Rsbuild configuration for [CRA configuration](https://create-react-app.dev/docs/advanced-configuration/):

| CRA                     | Rsbuild                                                                                         |
| ----------------------- | ----------------------------------------------------------------------------------------------- |
| HOST                    | [server.host](/config/server/host)                                                              |
| PORT                    | [server.port](/config/server/port)                                                              |
| HTTPS                   | [server.https](/config/server/https)                                                            |
| WDS_SOCKET_HOST         | [dev.client.host](/config/dev/client)                                                           |
| WDS_SOCKET_PATH         | [dev.client.path](/config/dev/client)                                                           |
| WDS_SOCKET_PORT         | [dev.client.port](/config/dev/client)                                                           |
| PUBLIC_URL              | [dev.assetPrefix](/config/dev/asset-prefix) / [output.assetPrefix](/config/output/asset-prefix) |
| BUILD_PATH              | [output.distPath](/config/output/dist-path)                                                     |
| GENERATE_SOURCEMAP      | [output.sourceMap](/config/output/source-map)                                                   |
| IMAGE_INLINE_SIZE_LIMIT | [output.dataUriLimit](/config/output/data-uri-limit)                                            |
| FAST_REFRESH            | [dev.hmr](/config/dev/hmr)                                                                      |
| TSC_COMPILE_ON_ERROR    | [@rsbuild/plugin-type-check](https://github.com/rspack-contrib/rsbuild-plugin-type-check)       |

Notes:

- The above table does not cover all configurations of CRA, feel free to add more.

## Compile node_modules

By default, CRA uses Babel to compile dependencies in node_modules, but Rsbuild does not, to avoid the performance overhead and potential compilation errors caused by secondary compilation.

If you need to handle syntax compatibility issues caused by dependencies in node_modules, you can use the [source.include](/config/source/include#compile-node_modules) config to compile node_modules.

## Env Variables

CRA injects environment variables starting with `REACT_APP_` into the client code by default, while Rsbuild injects environment variables starting with `PUBLIC_` by default (see [public variables](/guide/advanced/env-vars#public-variables)).

To be compatible with CRA's behavior, you can manually call Rsbuild's [loadEnv](/api/javascript-api/core#loadenv) method to read environment variables starting with `REACT_APP_`, and inject them into the client code through the [source.define](/config/source/define) config.

```ts title="rsbuild.config.ts"
import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: publicVars,
  },
});
```

Note that CRA allows access to the full `process.env` object in the code and also allows destructuring of `process.env`. However, Rsbuild does not define the `process.env` object due to bundle size and security concerns.

```ts title="src/index.js"
// In CRA, you can access it like this
const { PUBLIC_URL } = process.env;
console.log(PUBLIC_URL);
console.log(process.env);
```

In Rsbuild, you can use the [source.define](/config/source/define) config to set `process.env` and read the `rawPublicVars` returned by the `loadEnv` method to allow the above usage:

```ts title="rsbuild.config.ts"
import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars, rawPublicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: {
      ...publicVars,
      'process.env': JSON.stringify(rawPublicVars),
    },
  },
});
```

## Import Unknown Assets

In CRA, if you import an asset that the build tool cannot recognize, CRA will by default output the file to the `build/static/media` directory, for example, the `document.pdf` file:

```js title="index.js"
import document from './document.pdf';
```

In Rsbuild, when importing unrecognized assets, Rsbuild will logs errors:

```text
You may need an appropriate loader to handle this file type.
```

To resolve this error, you can use the following methods:

- Configure a suitable loader to handle this type of asset via [tools.rspack](/config/tools/rspack).
- Configure [asset modules](https://rspack.dev/guide/features/asset-module) rule to handle this type of asset via [tools.rspack](/config/tools/rspack).

For example, you can add the following asset modules config to get the same output result as CRA:

```ts title="rsbuild.config.ts"
export default {
  tools: {
    rspack: {
      module: {
        rules: [
          {
            // Match .png asset
            // You can change this regular expression to match different types of files
            test: /\.png$/,
            type: 'asset/resource',
            generator: {
              filename: 'static/media/[name].[hash][ext]',
            },
          },
        ],
      },
    },
  },
};
```

## Remove react-app-polyfill

CRA provides [react-app-polyfill](https://www.npmjs.com/package/react-app-polyfill) to manually inject polyfill code.

In the Rsbuild project, you can remove the dependency and code related to react-app-polyfill, as Rsbuild will automatically read the browserslist config and inject polyfill code as needed.

```diff title="src/index.js"
- import 'react-app-polyfill/ie11';
- import 'react-app-polyfill/stable';
```

You can read [Browser Compatibility](/guide/advanced/browser-compatibility) to understand how Rsbuild handles polyfills.

## Add ESLint plugin

CRA has the [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin) enabled by default and includes a set of built-in ESLint rules.

In an Rsbuild project, if you need similar functionality, you can add [@rsbuild/plugin-eslint](https://github.com/rspack-contrib/rsbuild-plugin-eslint) and use [eslint-config-react-app](https://www.npmjs.com/package/eslint-config-react-app) as the ESLint ruleset.

```js title=".eslintrc.cjs"
module.exports = {
  extends: 'react-app',
};
```

## Reading jsconfig.json

In non-TypeScript projects, CRA supports reading the `paths` field in jsconfig.json as the path alias.

If you want to use this feature in Rsbuild, you can set the [source.tsconfigPath](/config/source/tsconfig-path) option, so that Rsbuild can recognize the `paths` field in jsconfig.json.

```js title="rsbuild.config.mjs"
export default {
  source: {
    tsconfigPath: './jsconfig.json',
  },
};
```

## CRACO Migration

If your project is using [CRACO](https://craco.js.org) to override CRA configuration, you can refer to the table below for migration:

| CRACO                                                                                           | Rsbuild                                                                                   |
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [webpack.configure](https://craco.js.org/docs/configuration/webpack/#webpackconfigure)          | [tools.rspack](/config/tools/rspack)                                                      |
| [webpack.alias](https://craco.js.org/docs/configuration/webpack/#webpackalias)                  | [resolve.alias](/config/resolve/alias)                                                    |
| [webpack.plugins.add](https://craco.js.org/docs/configuration/webpack/#webpackplugins)          | [appendPlugins of tools.rspack](/config/tools/rspack#appendplugins)                       |
| [webpack.plugins.remove](https://craco.js.org/docs/configuration/webpack/#webpackpluginsremove) | [removePlugin of tools.rspack](/config/tools/rspack#removeplugin)                         |
| [style.modules](https://craco.js.org/docs/configuration/style/#stylemodules)                    | [output.cssModules](/config/output/css-modules)                                           |
| [style.css](https://craco.js.org/docs/configuration/style/#stylecss)                            | [tools.cssLoader](/config/tools/css-loader)                                               |
| [style.sass](https://craco.js.org/docs/configuration/style/#stylesass)                          | [Sass Plugin](/plugins/list/plugin-sass)                                                  |
| [style.postcss](https://craco.js.org/docs/configuration/style/#stylepostcss)                    | [tools.postcss](/config/tools/postcss)                                                    |
| [babel](https://craco.js.org/docs/configuration/babel/)                                         | [Babel Plugin](/plugins/list/plugin-babel)                                                |
| [typescript](https://craco.js.org/docs/configuration/typescript/)                               | [@rsbuild/plugin-type-check](https://github.com/rspack-contrib/rsbuild-plugin-type-check) |
| [devServer](https://craco.js.org/docs/configuration/devserver/)                                 | [server configs](/config/index)                                                           |

### Example

Here is an example of migrating from `webpack.configure` to `tools.rspack`:

- Before migration:

```js title="craco.config.js"
const { whenDev } = require('@craco/craco');

module.exports = {
  webpack: {
    configure: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
    },
    plugins: [...whenDev(() => [new MyWebpackPlugin()], [])],
  },
};
```

- After migration:

```ts title="rsbuild.config.ts"
export default {
  tools: {
    rspack: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
      plugins:
        process.env.NODE_ENV === 'development' ? [new MyWebpackPlugin()] : [],
    },
  },
};
```

## Contents Supplement

The current document only covers part of the migration process. If you find suitable content to add, feel free to contribute to the documentation via pull request 🤝.

> The documentation for rsbuild can be found in the [rsbuild/website](https://github.com/web-infra-dev/rsbuild/tree/main/website) directory.
